﻿/// -----------------------------------------------------------------------------------------------------------
/// Module      :  SchemaQualifiedType.cs
/// Description :  This helper class represents the base class for types defined in Xml Schemas
/// -----------------------------------------------------------------------------------------------------------

#region Using directives

using System;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;

using Microsoft.ServiceModel.Channels.Common;

#endregion

namespace BizTalkFactory.Adapters.AzureStorage
{
    public class SchemaQualifiedType : TypeMetadata
    {
        public SchemaQualifiedType(string typeId, string typeName)
            : base(typeId, typeName)
        {
            Description = String.Empty;
            CanUseCommonCache = true;
            // if the nillable is not set to true, the generated proxy wraps the operation
            // with request and response objects
            IsNillable = true;
        }

        #region TypeMetadata Implementation

        /// <summary>
        /// Gets a Boolean value indicating whether a default value can be setE:\Dev\Azure\AzureStorageAdapter\AzureStorageAdapter\End\AzureStorageAdapter\SchemaQualifiedType.cs
        /// for an operation parameter, operation result, and/or a type member
        /// </summary>
        public override bool CanSetDefaultValue
        {
            get { return false; }
        }

        /// <summary>
        /// Exports the type's schema to the SchemaSet and the namespace prefixes to the NamespacePrefixSet of the XmlSchemaExportContext
        /// </summary>
        /// <param name="schemaExportContext">The XmlSchemaExportContext that contains the SchemaSet and the NamespacePrefixSet that the type uses to export its schema and namespaces prefixes</param>
        /// <param name="metadataLookup">The metadata lookup used to look up other metadata definitions</param>
        /// <param name="timeout">The maximum time to wait for the completion of the operation  </param>
        public override void ExportXmlSchema(XmlSchemaExportContext schemaExportContext, MetadataLookup metadataLookup, TimeSpan timeout)
        {
            if (schemaExportContext == null)
                throw new AdapterException(Resources.Err_Invalid_XmlSchemaExportContext);

            // create XmlSchema object from string resource template

            string resource = String.Format("{0}.xsd", GetType().FullName);

            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource))
            using (StreamReader sReader = new StreamReader(stream))
            using (XmlReader xReader = XmlReader.Create(new StringReader(ReplaceTokens(sReader.ReadToEnd()))))
            {
                XmlSchema schema = XmlSchema.Read(xReader, null);

                if (!IsComplexTypeAlreadyDefined(schemaExportContext.SchemaSet, schema))
                {
                    schemaExportContext.SchemaSet.Add(schema);
                    if (!schemaExportContext.NamespacePrefixSet.ContainsKey(TypeNamespace))
                        schemaExportContext.NamespacePrefixSet.Add(TypeNamespace, GetUniqueNamespacePrefix(schemaExportContext, 0));
                }
            }
        }

        #endregion

        protected virtual string ReplaceTokens(string format)
        {
            return format;
        }

        /// <summary>
        /// Gets the fully qualified type identifier
        /// </summary>
        /// <param name="storagePrefix">Prefix of the storage type, with a trailing slash "/" character</param>
        /// <param name="operationName">Name of the operation</param>
        /// <returns></returns>
        public static string TypeId(string storagePrefix, string operationName)
        {
            return String.Format("{0}/{1}{2}/{3}"
                , StorageAdapterConstants.MetadataTypeIds.GenericType
                , storagePrefix.EndsWith("/") ? storagePrefix : storagePrefix + "/"
                , operationName
                , StorageAdapterConstants.WsdlBuilderConstants.OPERATION_REQUEST_SUFFIX
                );
        }

        /// <summary>
        /// Helper function to see if the schema is already defined in the XmlSchemaSet.
        /// </summary>
        /// <param name="oldschemaset"></param>
        /// <param name="newschema"></param>
        /// <returns></returns>
        public static bool IsComplexTypeAlreadyDefined(XmlSchemaSet oldschemaset, XmlSchema newschema)
        {
            // ensure correct namespace was defined in the passed-in schema
            foreach (XmlSchema schema in oldschemaset.Schemas(newschema.TargetNamespace))
            foreach (XmlSchemaObject newschemaObject in newschema.Items)
            {
                if (newschemaObject is XmlSchemaComplexType)
                {
                    //check for the definition of complex type in the schemaset           
                    foreach (XmlSchemaObject schemaObject in schema.Items)
                    {
                        // Definition of this Complex Type already exists
                        XmlSchemaComplexType complexType = schemaObject as XmlSchemaComplexType;
                        if (complexType != null && String.Compare(complexType.Name, ((XmlSchemaComplexType)newschemaObject).Name, false, System.Globalization.CultureInfo.InvariantCulture) == 0)
                            return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Helper function to generate a unique namespace prefix
        /// </summary>
        /// <param name="schemaExportContext"></param>
        /// <param name="startSuffix"></param>
        /// <returns></returns>
        private static string GetUniqueNamespacePrefix(XmlSchemaExportContext schemaExportContext, int startSuffix)
        {
            string defaultPrefix = "ns";
            string val = defaultPrefix + startSuffix;
            if (schemaExportContext.NamespacePrefixSet.ContainsValue(val))
                return GetUniqueNamespacePrefix(schemaExportContext, ++startSuffix);

            return val;
        }
    }
}
